Jx.declare('Jx.lib.Detect', {

	$requires: ['code.platform.tokenizer'],

	$accessors: {

		capable: false,

		name: 'Unknown',

		version: '0'

	},

	$actions: [

		'detect',

		'setup'

	],

	$helpers: {

		'Number': 'Jx.lib.Number'

	},

	autoDetect: true,

	queryCache: null,

	tokenLookup: null,

	caseSensitiveQuery: false,

	tokenizerClass: 'code.platform.tokenizer',

	defaultBaseReference: 'version',

	ignoreTokenLookup: [ 'version' ],

	versionReferences: {

		'name': 'version'

	},

	tokenizer: undefined,

	defaultBaseReference: 'version',

	defaultBaseOperator: '=',

	platformClass: 'Jx.Platform',

	compareCallbacks: {

		'version': function(baseValue, operator, token){

			return this.$Number.compare(operator, baseValue, token, operator != '=');

		},

		'*': function(baseValue, operator, token){

			return Jx.isBoolean(baseValue) ?

				baseValue

				:

				baseValue == token;

		}

	},

	constructor: function(){

		this.queryCache = {};

		this.tokenizer = Jx.instantiate(this.tokenizerClass);

		if (this.autoDetect){

			this.detect();

		}

	},

	initTokenLookup: function(values, lookup){

		var ignored = this.ignoreTokenLookup;

		if (!Jx.isArray(ignored)){

			ignored = this.ignoreTokenLookup = [];

		}

		Jx.each(values, function(n, v, o){

			if (Jx.isBoolean(v)){

				v = n;

			}

			if (Jx.isString(v) && this.ignoreTokenLookup.indexOf(n) == -1){

				if (!this.caseSensitiveQuery){

					v = v.toLowerCase();

				}

				this.tokenLookup[v] = n;

			}

		}, this);

	},

	onAfterDetect: function(){

		this.initTokenLookup( this.$cache, this.tokenLookup = {});

	},

	onExecQuery: function(query, operation){

		var tokenizer = this.tokenizer,

			callbacks = this.compareCallbacks,

			lookup = this.tokenLookup,

			caseSensitive = this.caseSensitiveQuery,

			ref = this.defaultBaseReference,

			operator = this.defaultBaseOperator,

			refs = this.versionReferences,

			value, baseValue, operate;

		tokenizer.set(query);

		for(var token, o; o = tokenizer.next(); ){

			value = o.value;

			token = o.token;

			operate = false;

			//-- operator
			if (token.substring(0, 2) == 'op'){

				operator = o.value;

			//-- version/number/float
			} else if (token == 'number' || token == 'float' || token == 'version'){

				baseValue = this.get( operate = ref );

			//-- string literals
			} else if (token == 'string'){

				if (!caseSensitive){

					value = value.toLowerCase()

				}

				//-- has lookup property
				if (lookup.hasOwnProperty(value)){

					operate = lookup[value];

					baseValue = this.get( operate );

					//-- shift reference
					if (refs.hasOwnProperty(operate)){

						ref = refs[ operate ];

					}

				}

			}

			//-- evaluate last baseValue
			if (operate){

				if (!callbacks.hasOwnProperty(operate)){

					//-- use catch all compare
					operate = '*';

				}

				if (Jx.isString(baseValue) && !caseSensitive){

					baseValue = baseValue.toLowerCase();

				}

				operation.result = callbacks[operate].call(this, baseValue, operator, value) !== false;

				if (!operation.result){

					break;

				}

			}

		}

	},

	onDetect: function(){

		Jx.each(this.$cache, this.handleEachAccessor, this);

	},

	onSetup: function(operation, platform){



	},

	allowDetect: function(operation){

		return this.capable();

	},

	allowSetup: function(operation, platform){

		return Jx.instanceOf(platform, this.platformClass);

	},

	handleEachAccessor: function(name, defaultValue, cache){

		var camelized = name.charAt(0).toUpperCase() + name.substring(1, name.length),

			methodName = 'detect' + camelized,

			method;

		if (methodName in this &&

			Jx.isFunction(method = this[methodName])

		){

			this[ name ](

				method.call( this, defaultValue )

			);

		}

	},

	is: function(query){

		var cache = this.queryCache,

			result = false,

			operation;

		if (Jx.isString(query)){

			if (!cache.hasOwnProperty(query)){

				operation = {

					result: false

				};

				if (query){

					this.onExecQuery(query, operation);

				}

				cache[query] = operation.result;



			}

			result = cache[query];

		}

		return result;

	}



});
